from typing import List, Dict, Optional, Tuple
from decimal import Decimal
from datetime import date
from stocks.models import Strategy, BuySignal, Stock
from stocks.services.technical_analysis import StockDataProcessor, TechnicalAnalysis


class SignalGenerator:
    """信号生成器基类"""
    
    def __init__(self, strategy: Strategy):
        self.strategy = strategy
        self.params = strategy.parameters
    
    def generate_signals(self, symbol: str) -> List[Dict]:
        """
        生成买入信号
        
        Args:
            symbol: 股票代码
            
        Returns:
            买入信号列表
        """
        raise NotImplementedError("子类必须实现此方法")
    
    def calculate_signal_strength(self, signal_data: Dict) -> float:
        """
        计算信号强度 (0-100)
        
        Args:
            signal_data: 信号数据
            
        Returns:
            信号强度值
        """
        raise NotImplementedError("子类必须实现此方法")
    
    def get_reason(self, signal_data: Dict) -> str:
        """
        获取买入理由描述
        
        Args:
            signal_data: 信号数据
            
        Returns:
            买入理由描述
        """
        raise NotImplementedError("子类必须实现此方法")


class MovingAverageStrategy(SignalGenerator):
    """移动平均线策略"""
    
    def generate_signals(self, symbol: str) -> List[Dict]:
        short_period = self.params.get('short_period', 5)
        long_period = self.params.get('long_period', 20)
        threshold = self.params.get('threshold', 0.02)
        
        analysis_data = StockDataProcessor.prepare_analysis_data(symbol, days=long_period + 10)
        raw_data = analysis_data['raw_data']
        indicators = analysis_data['indicators']
        
        if not raw_data or len(raw_data) < long_period:
            return []
        
        ma_short = indicators.get(f'ma{short_period}', [])
        ma_long = indicators.get(f'ma{long_period}', [])
        
        signals = []
        
        # 检查最近的数据点
        for i in range(len(raw_data) - 1, max(len(raw_data) - 5, 0), -1):
            if (ma_short[i] is not None and ma_long[i] is not None and
                ma_short[i-1] is not None and ma_long[i-1] is not None):
                
                # 检查金叉条件：短期均线上穿长期均线
                if (ma_short[i-1] <= ma_long[i-1] and 
                    ma_short[i] > ma_long[i] and
                    raw_data[i]['close'] is not None):
                    
                    # 计算突破幅度
                    break_through = (ma_short[i] - ma_long[i]) / ma_long[i]
                    
                    if break_through >= threshold:
                        signal_data = {
                            'date': raw_data[i]['date'],
                            'price': raw_data[i]['close'],
                            'ma_short': ma_short[i],
                            'ma_long': ma_long[i],
                            'break_through': break_through,
                            'position': i
                        }
                        
                        signal_strength = self.calculate_signal_strength(signal_data)
                        reason = self.get_reason(signal_data)
                        
                        signals.append({
                            'signal_date': signal_data['date'],
                            'signal_price': signal_data['price'],
                            'signal_strength': signal_strength,
                            'reason': reason,
                            'signal_data': signal_data
                        })
        
        return signals
    
    def calculate_signal_strength(self, signal_data: Dict) -> float:
        """计算移动平均线策略的信号强度"""
        break_through = signal_data['break_through']
        
        # 突破幅度越大，信号强度越强
        base_strength = min(break_through * 500, 80)  # 最大80分基础分
        
        # 考虑均线斜率
        ma_short = signal_data['ma_short']
        ma_long = signal_data['ma_long']
        
        # 均线间距比例
        spacing_ratio = (ma_short - ma_long) / ma_long
        spacing_score = min(spacing_ratio * 1000, 20)  # 最大20分
        
        return min(base_strength + spacing_score, 100)
    
    def get_reason(self, signal_data: Dict) -> str:
        short_period = self.params.get('short_period', 5)
        long_period = self.params.get('long_period', 20)
        
        return (f"{short_period}日均线上穿{long_period}日均线，形成金叉信号。"
                f"突破幅度：{signal_data['break_through']*100:.2f}%")


class RSIStrategy(SignalGenerator):
    """RSI超卖策略"""
    
    def generate_signals(self, symbol: str) -> List[Dict]:
        oversold_level = self.params.get('oversold_level', 30)
        overbought_level = self.params.get('overbought_level', 70)
        rsi_period = self.params.get('rsi_period', 14)
        
        analysis_data = StockDataProcessor.prepare_analysis_data(symbol, days=rsi_period + 20)
        raw_data = analysis_data['raw_data']
        indicators = analysis_data['indicators']
        
        if not raw_data:
            return []
        
        rsi_values = indicators.get('rsi', [])
        
        signals = []
        
        # 检查最近的数据点
        for i in range(len(raw_data) - 1, max(len(raw_data) - 10, 0), -1):
            if (rsi_values[i] is not None and raw_data[i]['close'] is not None and
                i > 0 and rsi_values[i-1] is not None):
                
                # 检查超卖反弹条件
                if (rsi_values[i-1] <= oversold_level and 
                    rsi_values[i] > oversold_level and
                    rsi_values[i] < overbought_level):
                    
                    signal_data = {
                        'date': raw_data[i]['date'],
                        'price': raw_data[i]['close'],
                        'rsi_current': rsi_values[i],
                        'rsi_previous': rsi_values[i-1],
                        'position': i
                    }
                    
                    signal_strength = self.calculate_signal_strength(signal_data)
                    reason = self.get_reason(signal_data)
                    
                    signals.append({
                        'signal_date': signal_data['date'],
                        'signal_price': signal_data['price'],
                        'signal_strength': signal_strength,
                        'reason': reason,
                        'signal_data': signal_data
                    })
        
        return signals
    
    def calculate_signal_strength(self, signal_data: Dict) -> float:
        """计算RSI策略的信号强度"""
        rsi_current = signal_data['rsi_current']
        rsi_previous = signal_data['rsi_previous']
        
        # RSI从超卖区域反弹的幅度
        oversold_level = self.params.get('oversold_level', 30)
        rebound = max(0, rsi_current - oversold_level)
        
        # 反弹幅度越大，信号强度越强
        strength = min(rebound * 3, 60)  # 最大60分
        
        # 考虑反弹速度
        rebound_speed = rsi_current - rsi_previous
        speed_score = min(rebound_speed * 10, 20)  # 最大20分
        
        # 考虑当前位置（不要太接近超买区域）
        overbought_level = self.params.get('overbought_level', 70)
        position_score = max(0, (overbought_level - rsi_current) / overbought_level * 20)  # 最大20分
        
        return min(strength + speed_score + position_score, 100)
    
    def get_reason(self, signal_data: Dict) -> str:
        oversold_level = self.params.get('oversold_level', 30)
        
        return (f"RSI从超卖区域({oversold_level}以下)反弹，"
                f"当前RSI：{signal_data['rsi_current']:.1f}，"
                f"较前一日上升：{signal_data['rsi_current'] - signal_data['rsi_previous']:.1f}")


class VolumeBreakoutStrategy(SignalGenerator):
    """成交量突破策略"""
    
    def generate_signals(self, symbol: str) -> List[Dict]:
        volume_multiplier = self.params.get('volume_multiplier', 2.0)
        lookback_days = self.params.get('lookback_days', 20)
        min_price_change = self.params.get('min_price_change', 0.03)
        
        analysis_data = StockDataProcessor.prepare_analysis_data(symbol, days=lookback_days + 10)
        raw_data = analysis_data['raw_data']
        
        if not raw_data or len(raw_data) < lookback_days:
            return []
        
        signals = []
        
        # 检查最近的数据点
        for i in range(len(raw_data) - 1, max(len(raw_data) - 5, 0), -1):
            if (raw_data[i]['volume'] is not None and raw_data[i]['close'] is not None and
                i >= lookback_days):
                
                # 计算平均成交量
                avg_volume = sum(d['volume'] for d in raw_data[i-lookback_days:i] if d['volume'] is not None) / lookback_days
                
                # 检查成交量突破条件
                if (raw_data[i]['volume'] > avg_volume * volume_multiplier and
                    raw_data[i]['close'] > raw_data[i-1]['close'] * (1 + min_price_change)):
                    
                    signal_data = {
                        'date': raw_data[i]['date'],
                        'price': raw_data[i]['close'],
                        'volume': raw_data[i]['volume'],
                        'avg_volume': avg_volume,
                        'volume_ratio': raw_data[i]['volume'] / avg_volume,
                        'price_change': (raw_data[i]['close'] - raw_data[i-1]['close']) / raw_data[i-1]['close'],
                        'position': i
                    }
                    
                    signal_strength = self.calculate_signal_strength(signal_data)
                    reason = self.get_reason(signal_data)
                    
                    signals.append({
                        'signal_date': signal_data['date'],
                        'signal_price': signal_data['price'],
                        'signal_strength': signal_strength,
                        'reason': reason,
                        'signal_data': signal_data
                    })
        
        return signals
    
    def calculate_signal_strength(self, signal_data: Dict) -> float:
        """计算成交量突破策略的信号强度"""
        volume_ratio = signal_data['volume_ratio']
        price_change = signal_data['price_change']
        
        # 成交量倍数越大，信号强度越强
        volume_score = min((volume_ratio - 1) * 25, 60)  # 最大60分
        
        # 价格涨幅越大，信号强度越强
        price_score = min(price_change * 1000, 30)  # 最大30分
        
        # 考虑绝对成交量大小
        volume = signal_data['volume']
        volume_size_score = min(volume / 1000000 * 0.1, 10)  # 最大10分
        
        return min(volume_score + price_score + volume_size_score, 100)
    
    def get_reason(self, signal_data: Dict) -> str:
        volume_multiplier = self.params.get('volume_multiplier', 2.0)
        
        return (f"成交量突破{volume_multiplier}倍均量，"
                f"成交量比：{signal_data['volume_ratio']:.2f}倍，"
                f"价格上涨：{signal_data['price_change']*100:.2f}%")


class KDJStrategy(SignalGenerator):
    """KDJ指标策略"""
    
    def generate_signals(self, symbol: str) -> List[Dict]:
        k_period = self.params.get('k_period', 9)
        d_period = self.params.get('d_period', 3)
        j_period = self.params.get('j_period', 3)
        oversold_level = self.params.get('oversold_level', 20)
        overbought_level = self.params.get('overbought_level', 80)
        
        analysis_data = StockDataProcessor.prepare_analysis_data(symbol, days=k_period + 20)
        raw_data = analysis_data['raw_data']
        indicators = analysis_data['indicators']
        
        if not raw_data:
            return []
        
        k_values = indicators.get('k', [])
        d_values = indicators.get('d', [])
        j_values = indicators.get('j', [])
        
        signals = []
        
        # 检查最近的数据点
        for i in range(len(raw_data) - 1, max(len(raw_data) - 10, 0), -1):
            if (k_values[i] is not None and d_values[i] is not None and j_values[i] is not None and
                raw_data[i]['close'] is not None and i > 0 and
                k_values[i-1] is not None and d_values[i-1] is not None):
                
                # 检查KDJ金叉条件
                if (k_values[i-1] <= d_values[i-1] and 
                    k_values[i] > d_values[i] and
                    k_values[i] <= oversold_level and
                    j_values[i] <= oversold_level):
                    
                    signal_data = {
                        'date': raw_data[i]['date'],
                        'price': raw_data[i]['close'],
                        'k_value': k_values[i],
                        'd_value': d_values[i],
                        'j_value': j_values[i],
                        'position': i
                    }
                    
                    signal_strength = self.calculate_signal_strength(signal_data)
                    reason = self.get_reason(signal_data)
                    
                    signals.append({
                        'signal_date': signal_data['date'],
                        'signal_price': signal_data['price'],
                        'signal_strength': signal_strength,
                        'reason': reason,
                        'signal_data': signal_data
                    })
        
        return signals
    
    def calculate_signal_strength(self, signal_data: Dict) -> float:
        """计算KDJ策略的信号强度"""
        k_value = signal_data['k_value']
        d_value = signal_data['d_value']
        j_value = signal_data['j_value']
        
        # KDJ值越低（超卖越严重），信号强度越强
        oversold_level = self.params.get('oversold_level', 20)
        oversold_score = max(0, (oversold_level - k_value) / oversold_level * 40)  # 最大40分
        
        # KDJ金叉幅度越大，信号强度越强
        cross_strength = (k_value - d_value) / d_value
        cross_score = min(cross_strength * 500, 30)  # 最大30分
        
        # J值越低，信号强度越强
        j_score = max(0, (oversold_level - j_value) / oversold_level * 30)  # 最大30分
        
        return min(oversold_score + cross_score + j_score, 100)
    
    def get_reason(self, signal_data: Dict) -> str:
        return (f"KDJ指标在超卖区域形成金叉，"
                f"K值：{signal_data['k_value']:.1f}，"
                f"D值：{signal_data['d_value']:.1f}，"
                f"J值：{signal_data['j_value']:.1f}")


class MultiMAStrategy(SignalGenerator):
    """均线多头排列策略"""
    
    def generate_signals(self, symbol: str) -> List[Dict]:
        ma_periods = self.params.get('ma_periods', [5, 10, 20, 60])
        min_uptrend_days = self.params.get('min_uptrend_days', 3)
        
        analysis_data = StockDataProcessor.prepare_analysis_data(symbol, days=max(ma_periods) + 10)
        raw_data = analysis_data['raw_data']
        indicators = analysis_data['indicators']
        
        if not raw_data or len(raw_data) < max(ma_periods):
            return []
        
        signals = []
        
        # 检查最近的数据点
        for i in range(len(raw_data) - 1, max(len(raw_data) - 10, 0), -1):
            # 检查所有均线数据是否齐全
            ma_values = {}
            for period in ma_periods:
                ma_key = f'ma{period}'
                if ma_key not in indicators or indicators[ma_key][i] is None:
                    break
                ma_values[period] = indicators[ma_key][i]
            else:
                # 检查多头排列条件：短周期均线 > 长周期均线
                is_multi_ma = True
                for j in range(len(ma_periods) - 1):
                    if ma_values[ma_periods[j]] <= ma_values[ma_periods[j+1]]:
                        is_multi_ma = False
                        break
                
                if is_multi_ma and raw_data[i]['close'] is not None:
                    # 检查是否持续多头排列
                    uptrend_count = 0
                    for k in range(min_uptrend_days):
                        if i - k < 0:
                            break
                        current_ok = True
                        for j in range(len(ma_periods) - 1):
                            if (indicators[f'ma{ma_periods[j]}'][i-k] is None or
                                indicators[f'ma{ma_periods[j+1]}'][i-k] is None or
                                indicators[f'ma{ma_periods[j]}'][i-k] <= indicators[f'ma{ma_periods[j+1]}'][i-k]):
                                current_ok = False
                                break
                        if current_ok:
                            uptrend_count += 1
                    
                    if uptrend_count >= min_uptrend_days:
                        signal_data = {
                            'date': raw_data[i]['date'],
                            'price': raw_data[i]['close'],
                            'ma_values': ma_values,
                            'uptrend_days': uptrend_count,
                            'position': i
                        }
                        
                        signal_strength = self.calculate_signal_strength(signal_data)
                        reason = self.get_reason(signal_data)
                        
                        signals.append({
                            'signal_date': signal_data['date'],
                            'signal_price': signal_data['price'],
                            'signal_strength': signal_strength,
                            'reason': reason,
                            'signal_data': signal_data
                        })
        
        return signals
    
    def calculate_signal_strength(self, signal_data: Dict) -> float:
        """计算均线多头排列策略的信号强度"""
        uptrend_days = signal_data['uptrend_days']
        ma_values = signal_data['ma_values']
        
        # 多头排列持续时间越长，信号强度越强
        duration_score = min(uptrend_days * 15, 50)  # 最大50分
        
        # 均线间距越大，信号强度越强
        spacing_score = 0
        periods = sorted(ma_values.keys())
        for i in range(len(periods) - 1):
            spacing = (ma_values[periods[i]] - ma_values[periods[i+1]]) / ma_values[periods[i+1]]
            spacing_score += min(spacing * 100, 10)  # 每对均线最大10分
        
        return min(duration_score + spacing_score, 100)
    
    def get_reason(self, signal_data: Dict) -> str:
        periods = sorted(signal_data['ma_values'].keys())
        period_str = '/'.join(str(p) for p in periods)
        
        return (f"{period_str}日均线形成多头排列，"
                f"已持续{signal_data['uptrend_days']}天")


class StrategyExecutor:
    """策略执行器"""
    
    @staticmethod
    def get_strategy_class(strategy_type: str) -> SignalGenerator:
        """根据策略类型获取对应的策略类"""
        strategy_classes = {
            'ma': MovingAverageStrategy,
            'rsi': RSIStrategy,
            'volume': VolumeBreakoutStrategy,
            'kdj': KDJStrategy,
            'multi_ma': MultiMAStrategy,
            # 可以继续添加其他策略类型
        }
        
        return strategy_classes.get(strategy_type, SignalGenerator)
    
    @staticmethod
    def execute_strategy(strategy_id: int, symbol: str) -> List[Dict]:
        """
        执行特定策略
        
        Args:
            strategy_id: 策略ID
            symbol: 股票代码
            
        Returns:
            生成的信号列表
        """
        try:
            strategy = Strategy.objects.get(id=strategy_id)
            strategy_class = StrategyExecutor.get_strategy_class(strategy.strategy_type)
            
            if strategy_class == SignalGenerator:
                return []  # 未知策略类型
            
            generator = strategy_class(strategy)
            return generator.generate_signals(symbol)
            
        except Strategy.DoesNotExist:
            return []
        except Exception as e:
            print(f"执行策略时出错: {e}")
            return []
    
    @staticmethod
    def execute_all_strategies(symbol: str) -> List[Dict]:
        """
        对所有启用策略执行分析
        
        Returns:
            所有策略生成的信号列表
        """
        all_signals = []
        
        try:
            strategies = Strategy.objects.filter(is_active=True)
            
            for strategy in strategies:
                signals = StrategyExecutor.execute_strategy(strategy.id, symbol)
                for signal in signals:
                    signal['strategy_id'] = strategy.id
                    signal['strategy_name'] = strategy.name
                    signal['symbol'] = symbol  # 添加股票代码
                all_signals.extend(signals)
                
        except Exception as e:
            print(f"执行所有策略时出错: {e}")
        
        # 按信号强度排序
        all_signals.sort(key=lambda x: x['signal_strength'], reverse=True)
        return all_signals
    
    @staticmethod
    def save_signals(signals: List[Dict]) -> int:
        """
        保存买入信号到数据库
        
        Returns:
            保存的信号数量
        """
        saved_count = 0
        
        for signal in signals:
            try:
                strategy = Strategy.objects.get(id=signal['strategy_id'])
                stock = Stock.objects.get(symbol=signal.get('symbol'))
                
                # 检查是否已存在相同日期的信号
                existing_signal = BuySignal.objects.filter(
                    strategy=strategy,
                    stock=stock,
                    signal_date=signal['signal_date']
                ).first()
                
                if not existing_signal:
                    BuySignal.objects.create(
                        strategy=strategy,
                        stock=stock,
                        signal_date=signal['signal_date'],
                        signal_price=Decimal(str(signal['signal_price'])),
                        signal_strength=Decimal(str(signal['signal_strength'])),
                        reason=signal['reason']
                    )
                    saved_count += 1
                    
            except Exception as e:
                print(f"保存信号时出错: {e}")
                continue
        
        return saved_count
